package com.tianyadu.mmall.browser.conf;

import com.tianyadu.mmall.core.authentication.AbstractChannelSecurityConfig;
import com.tianyadu.mmall.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig;
import com.tianyadu.mmall.core.conf.SecurityConstantsConfig;
import com.tianyadu.mmall.core.conf.ValidateCodeSecurityConfig;
import com.tianyadu.mmall.core.properties.MallSecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * 浏览器配置
 *
 * @author xueyj
 * @date 2019-10-25 15:22
 */
@Configuration
public class BrowserSecurityConfig extends AbstractChannelSecurityConfig {

    /**
     * 简易登录认证formLogin/httpBasic
     * <p>
     * 指定身份认证方式，所有请求都需要授权认证
     * 认证方式一：http.formLogin() 表单登陆认证
     * 认证方式二: http.httpBasic() httpBasic登陆认证
     * and()：权限认证关联条件
     * authorizeRequests()：请求需要授权
     * anyRequest()：任何请求信息
     * authenticated()：都需要身份认证
     *
     * @param http
     * @author xueyj
     * @date 2019/10/8-14:48
     */
   /* @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //  form表单登录认证
                .formLogin()
                //  权限认证关联条件
                .and()
                //  认证授权请求
                .authorizeRequests()
                //  任何请求
                .anyRequest()
                //  都需要身份认证
                .authenticated();
    }*/

    /**
     * 自定义登录页面登录，注意静态资源加载相关配置处理，登录时跨站请求伪造防护禁用
     * <p>
     * 指定身份认证方式，所有请求都需要授权认证
     * 认证方式一：http.formLogin() 表单登陆认证
     * 认证方式二: http.httpBasic() httpBasic登陆认证
     * .loginPage("/demo-signIn.html") 自定义登录页面
     * and()：权限认证关联条件
     * authorizeRequests()：请求需要授权
     * antMatchers("/demo-signIn.html").permitAll() 当前路径不需要身份认证即可访问
     * anyRequest()：任何请求信息
     * authenticated()：都需要身份认证
     * .csrf().disable()：跨站请求伪造防护禁用，
     *
     * @param http
     * @author xueyj
     * @date 2019/10/8-14:49
     */
    /*@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //  form表单登录认证
                .formLogin()
                //  自定义登录页
                .loginPage("/demo-signIn.html")
                //  调用usernamePassword过滤器
                .loginProcessingUrl("/authentication/form")
                //  权限认证关联条件
                .and()
                //  认证授权请求
                .authorizeRequests()
                //  当前路径不需要身份认证即可访问
                .antMatchers("/demo-signIn.html").permitAll()
                //  任何请求
                .anyRequest()
                //  都需要身份认证
                .authenticated()
                .and()
                //  跨站请求伪造防护禁用
                .csrf().disable();
    }*/

    /**
     * 自定义登录页面，登录逻辑处理，注意静态资源加载相关配置处理，登录时跨站请求伪造防护禁用
     * <p>
     * 指定身份认证方式，所有请求都需要授权认证
     * 认证方式一：http.formLogin() 表单登陆认证
     * 认证方式二: http.httpBasic() httpBasic登陆认证
     * .loginPage("/authentication/require") 自定义登录页面处理URL
     * .loginProcessingUrl("/authentication/form")  调用usernamePassword过滤器
     * and()：权限认证关联条件
     * authorizeRequests()：请求需要授权
     * antMatchers("/authentication/form").permitAll() 当前路径不需要身份认证即可访问
     * anyRequest()：任何请求信息
     * authenticated()：都需要身份认证
     * .csrf().disable()：跨站请求伪造防护禁用，
     *
     * @param http
     * @author xueyj
     * @date 2019/10/8-14:49
     */
   /* @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 默认登录页
        String loginPage = securityProperties.getBrowser().getLoginPage();
        http
                //  form表单登录认证
                .formLogin()
                //  自定义登录页
                .loginPage("/authentication/require")
                //  调用usernamePassword过滤器
                .loginProcessingUrl("/authentication/form")
                //  权限认证关联条件
                .and()
                //  认证授权请求
                .authorizeRequests()
                //  当前路径不需要身份认证即可访问
                .antMatchers("/authentication/require", loginPage).permitAll()
                //  任何请求
                .anyRequest()
                //  都需要身份认证
                .authenticated()
                .and()
                //  跨站请求伪造防护禁用
                .csrf().disable();
    }*/

    /**
     * 认证成功处理器
     */
    @Autowired
    protected AuthenticationSuccessHandler authSuccessHandler;
    /**
     * 认证失败处理器
     */
    @Autowired
    protected AuthenticationFailureHandler authFailureHandler;
    /**
     * 获取security配置信息
     */
    @Autowired
    private MallSecurityProperties securityProperties;

    /**
     * 自定义登录页面，登录逻辑处理，注意静态资源加载相关配置处理，登录时跨站请求伪造防护禁用
     * <p>
     * 指定身份认证方式，所有请求都需要授权认证
     * http
     * * .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)  将图形验证码过滤器添加至用户名密码过滤器之前
     * 认证方式一：.formLogin() 表单登陆认证
     * 认证方式二: .httpBasic() httpBasic登陆认证
     * <p>
     * .loginPage("/authentication/require") 自定义登录页面处理URL
     * .loginProcessingUrl("/authentication/form")  调用usernamePassword过滤器
     * .successHandler(authSuccessHandler) 使用自定义授权认证成功处理器
     * .failureHandler(authFailureHandler) 使用自定义授权认证失败失败处理器
     * and()：权限认证关联条件
     * authorizeRequests()：请求需要授权
     * antMatchers("/authentication/form", loginPage).permitAll() 当前路径不需要身份认证即可访问
     * anyRequest()：任何请求信息
     * authenticated()：都需要身份认证
     * .csrf().disable()：跨站请求伪造防护禁用，
     *
     * @param http
     * @author xueyj
     * @date 2019/10/8-14:49
     */
    /*@Override
    protected void configure(HttpSecurity http) throws Exception {
        // 默认登录页
        String loginPage = securityProperties.getBrowser().getLoginPage();
        // 默认未验证Url,当请求需要身份认证时，默认跳转的url
        String defaultUnauthenticationUrl = SecurityConstantsConfig.DEFAULT_UNAUTHENTICATION_URL;
        // 默认的用户名密码登录请求处理url
        String defaultLoginProcessingUrlForm = SecurityConstantsConfig.DEFAULT_LOGIN_PROCESSING_URL_FORM;
        //  初始化验证码拦截器
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        //  添加验证失败拦截器
        validateCodeFilter.setAuthFailureHandler(authFailureHandler);
        //  添加安全认证配置信息
        validateCodeFilter.setSecurityProperties(securityProperties);
        //  添加安全认证初始化配置
        validateCodeFilter.afterPropertiesSet();
        http
                //  将图形验证码过滤器添加至用户名密码过滤器之前
                .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                //  form表单登录认证
                .formLogin()
                //  自定义登录页
                .loginPage(defaultUnauthenticationUrl)
                //  调用usernamePassword过滤器
                .loginProcessingUrl(defaultLoginProcessingUrlForm)
                //  使用自定义授权认证成功处理器
                .successHandler(authSuccessHandler)
                //  使用自定义授权认证失败失败处理器
                .failureHandler(authFailureHandler)
                //  权限认证关联条件
                .and()
                //  认证授权请求
                .authorizeRequests()
                //  当前路径不需要身份认证即可访问
                .antMatchers(
                        defaultUnauthenticationUrl,
                        loginPage,
                        "/code/image").permitAll()
                //  任何请求
                .anyRequest()
                //  都需要身份认证
                .authenticated()
                .and()
                //  跨站请求伪造防护禁用
                .csrf().disable();
    }*/

    /**
     * 数据库资源配置
     */
    @Autowired
    private DataSource dataSource;

    /**
     * 用户认证
     */
    @Autowired
    private UserDetailsService userDetailsService;
    /**
     * 验证码安全认证配置
     */
    @Autowired
    private ValidateCodeSecurityConfig validateCodeSecurityConfig;

    /**
     * 自定义登录页面，登录逻辑处理，注意静态资源加载相关配置处理，登录时跨站请求伪造防护禁用
     * 记住我功能实现：
     * 1.token存储器配置 # persistentTokenRepository,初始化记住我，需开启数据库配置或者手动执行sql
     * create
     * table persistent_logins
     * (username varchar(64) not null, series varchar(64) primary key,
     * token varchar(64) not null, last_used timestamp not null)
     * 2.数据源信息指定 #dataSource
     * 3.用户身份认证 #userDetailsService
     * 4.配置记住我相关配置项
     *
     * @param http
     * @author xueyj
     * @date 2019/10/8-14:49
     */
    /*@Override
    protected void configure(HttpSecurity http) throws Exception {
        // 登录页
        String loginPage = securityProperties.getBrowser().getLoginPage();
        // 记住我token有效期
        int rememberMeSeconds = securityProperties.getBrowser().getRememberMeSeconds();
        // 默认未验证Url,当请求需要身份认证时，默认跳转的url
        String defaultUnauthenticationUrl = SecurityConstantsConfig.DEFAULT_UNAUTHENTICATION_URL;
        // 默认的用户名密码登录请求处理url
        String defaultLoginProcessingUrlForm = SecurityConstantsConfig.DEFAULT_LOGIN_PROCESSING_URL_FORM;
        //  初始化验证码拦截器
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        //  添加验证失败拦截器
        validateCodeFilter.setAuthFailureHandler(authFailureHandler);
        //  添加安全认证配置信息
        validateCodeFilter.setSecurityProperties(securityProperties);
        //  添加安全认证初始化配置
        validateCodeFilter.afterPropertiesSet();
        http
                //  将图形验证码过滤器添加至用户名密码过滤器之前
                .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                //  form表单登录认证
                .formLogin()
                //  自定义登录页
                .loginPage(defaultUnauthenticationUrl)
                //  调用usernamePassword过滤器
                .loginProcessingUrl(defaultLoginProcessingUrlForm)
                //  使用自定义授权认证成功处理器
                .successHandler(authSuccessHandler)
                //  使用自定义授权认证失败失败处理器
                .failureHandler(authFailureHandler)
                //  权限认证关联条件
                .and()
                //  记住我相关配置
                .rememberMe()
                //  指定token读取数据源配置
                .tokenRepository(persistentTokenRepository())
                //  记住我token有效期
                .tokenValiditySeconds(rememberMeSeconds)
                //  根据token获取到用户信息后进行用户认证
                .userDetailsService(userDetailsService)
                .and()
                //  认证授权请求
                .authorizeRequests()
                //  当前路径不需要身份认证即可访问
                .antMatchers(
                        defaultUnauthenticationUrl,
                        loginPage,
                        "/code/*").permitAll()
                //  任何请求
                .anyRequest()
                //  都需要身份认证
                .authenticated()
                .and()
                //  跨站请求伪造防护禁用
                .csrf().disable();
    }*/
    @Autowired
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;

    /**
     * 记住我功能的token存取器配置
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        //  初始化数据库需要设置
        // tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

    /**
     * 安全认证配置
     *
     * @param http
     * @return void
     * @author xueyj
     * @date 2019/11/14-23:28
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 登录页
        String loginPage = securityProperties.getBrowser().getLoginPage();
        // 记住我token有效期
        int rememberMeSeconds = securityProperties.getBrowser().getRememberMeSeconds();
        // 默认未验证Url,当请求需要身份认证时，默认跳转的url
        String defaultUnauthenticationUrl = SecurityConstantsConfig.DEFAULT_UNAUTHENTICATION_URL;
        // 默认的用户名密码登录请求处理url
        String defaultLoginProcessingUrlForm = SecurityConstantsConfig.DEFAULT_LOGIN_PROCESSING_URL_FORM;
        // 默认的处理验证码的url前缀+"/*"
        String validateCodeUrl = SecurityConstantsConfig.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*";

        //  用户名密码登陆相关配置（登陆页面，验证成功/失败处理器）
        applyPasswordAuthenticationConfig(http);
        //  添加验证码相关拦截器配置
        http.apply(validateCodeSecurityConfig)
                .and()
                //  添加短信验证码相关拦截器配置
                .apply(smsCodeAuthenticationSecurityConfig)
                .and()
                //  添加记住我相关配置
                .rememberMe()
                //  指定token读取数据源配置
                .tokenRepository(persistentTokenRepository())
                //  记住我token有效期
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
                //  根据token获取到用户信息后进行用户认证
                .userDetailsService(userDetailsService)
                .and()
                //  认证授权请求
                .authorizeRequests()
                //  当前路径不需要身份认证即可访问
                .antMatchers(
                        loginPage,
                        defaultUnauthenticationUrl,
                        defaultLoginProcessingUrlForm,
                        validateCodeUrl
                ).permitAll()
                //  除无需认证请求外，任何请求都需要身份认证
                .anyRequest()
                //  都需要身份认证
                .authenticated()
                .and()
                //  跨站请求伪造防护禁用
                .csrf().disable();
    }
}